package com.sfx.distributeLockStock.lock;

import org.apache.zookeeper.*;
import org.apache.zookeeper.data.Stat;

import java.util.Collections;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: sfx
 * Date: 2024-02-05
 * Time: 14:52
 */
public class ZookeeperLock extends AbstractLock {

    private final ZooKeeper zookeeper;
    private final String lockName;
    private final String uuid;
    private String lockPath;
    private final ConcurrentHashMap<Thread, Integer> reentrantMap = new ConcurrentHashMap<>();

    public ZookeeperLock(ZooKeeper zooKeeper, String lockName) {
        this.zookeeper = zooKeeper;
        this.lockName = lockName;
        this.uuid = UUID.randomUUID().toString();
    }

    @Override
    public void lock(Long expireTime) {

    }

    Watcher watcher = watchedEvent -> {
        synchronized (ZookeeperLock.this) {
            //System.out.println("来唤醒了");
            ZookeeperLock.this.notify();
        }
    };

    @Override
    public void lock() {
//        // watch机制 + 临时节点
//        watchLock();

        // watcher + 顺序节点
        seqWatchLock();
    }

    public void seqWatchLock() {
        if (reentrantMap.containsKey(Thread.currentThread())) {
            Integer count = reentrantMap.get(Thread.currentThread());
            reentrantMap.put(Thread.currentThread(), ++count);
            return;
        }
        try {
            String node = zookeeper.create(lockPath(), uuid.getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
            lockPath = node;
            isFirstNode(node);
            reentrantMap.put(Thread.currentThread(), 1);
        } catch (Exception ignored) {
            // 获取锁失败
        }
    }

    //   /distributeLock/lockName000002
    public void isFirstNode(String node) {
        try {
            while (true) {
                //    /lockName000001
                //    /lockName000002
                //    /lockName000003
                //    /lockName000004
                //    /lockName000005
                List<String> children = zookeeper.getChildren(basePath(), null);
                Collections.sort(children);
                String[] split = node.split("/");
                //    /distributeLock
                //    /lockName000002
                int index = children.indexOf(split[split.length - 1]);
                if (index > 0) {
                    // 不是第一个
                    // watcher 前一个
                    synchronized (this) {
                        zookeeper.getData(children.get(index - 1), watcher, new Stat());
                        wait();
                    }
                }
                // index == 0
                // 是第一个
                break;
            }
        } catch (Exception ignored) {
        }
    }

    public void watchLock() {
        while (true) {
            try {
                // 临时节点
                String path = zookeeper.create(lockPath(), uuid.getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL, new Stat());
                if (lockPath().equals(path)) {
                    // 获取锁成功
                    break;
                }
            } catch (KeeperException keeperException) {
//                // 获取锁失败
//                try {
//                    // 睡眠100ms继续尝试获取锁
//                    Thread.sleep(100);
//                } catch (InterruptedException ex) {
//                    ex.printStackTrace();
//                }
                // 给节点添加一个watcher, 当加锁失败后,就会一直等待其他客户端释放锁,释放锁之后会notify唤醒
                try {
                    synchronized (this) {
                        zookeeper.getData(lockPath(), watcher, new Stat());
                        wait();
                    }
                } catch (Exception ignored) {
                }
            } catch (Exception e) {
                // 异常退出
                break;
            }
        }
    }

    @Override
    public void unlock() {
        if (reentrantMap.containsKey(Thread.currentThread())) {
            Integer count = reentrantMap.get(Thread.currentThread());
            if (--count > 0) {
                reentrantMap.put(Thread.currentThread(), count);
                return;
            }
            reentrantMap.remove(Thread.currentThread());
        }
        try {
            byte[] data = zookeeper.getData(lockPath, null, new Stat());
            if (uuid.equals(new String(data))) {
                // 释放锁成功
                zookeeper.delete(lockPath, -1);
            }
        } catch (Exception ignored) {
        }

    }

    public String basePath() {
        return "/" + "distribute-lock";
    }

    public String lockPath() {
        return basePath() + "/" + lockName;
    }
}